package com.zzyl.nursing.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.nursing.websocket.WebSocketServer;
import com.zzyl.system.mapper.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 报警规则Service业务层处理
 *
 * @author zjx
 * @date 2025-05-29
 */
@Service
@Slf4j
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private SysUserMapper userMapper;
    @Autowired
    private IAlertDataService alertDataService;

    @Autowired
    private WebSocketServer webSocketServer;
    @Value("${alert.role.admin}")
    private String roleAdmin;

    @Value("${alert.role.weixiu}")
    private String roleRepair;

    /**
     * 查询报警规则
     *
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     *
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     *
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     *
     * @param id 报警规则主键
     * @return 结果
     */

    @Override
    public int deleteAlertRuleById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 设备数据对应的报警规则校验，将报警数据保存到表中
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void alertRuleFilter() {
        //1.查询所有的规则并判断是否为空
        Long count = this.lambdaQuery().eq(AlertRule::getStatus, 1).count();
        if (count < 1) {
            log.error("[设备数据报警处理]规则为空,无需处理");
            return;
        }
        //2.查询redis上所有的设备数据并判断是否为空
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_DATA_LIST);
        if (CollUtil.isEmpty(values)) {
            log.error("[设备数据报警处理]设备数据为空,无需处理");
            return;
        }
        //3.对数据进行格式化处理
        List<DeviceData> deviceDataList = new ArrayList<>();
        values.forEach(v -> {
            List<DeviceData> dataList = JSONUtil.toList((String) v, DeviceData.class);
            deviceDataList.addAll(dataList);
        });

        for (DeviceData deviceData : deviceDataList) {

            //4.判断设备数据上报时间是否已经超过10分钟
            int limit = 6000;
            long between = LocalDateTimeUtil.between(deviceData.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS);
            if (between > limit) {
                log.error("[设备数据报警处理]设备数据上报时间已经超过10分钟,无需处理");
                continue;
            }
            //5.准备对应设备规则数据
            List<AlertRule> ruleList = this.lambdaQuery().eq(AlertRule::getIotId, -1)
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getStatus, 1)
                    .list();
            if (CollUtil.isEmpty(ruleList)) {
                ruleList = new ArrayList<>();
            }

            List<AlertRule> list = this.lambdaQuery().eq(AlertRule::getIotId, deviceData.getIotId())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getStatus, 1)
                    .list();
            if (CollUtil.isEmpty(list)) {
                list = new ArrayList<>();
            }

            ruleList.addAll(list);
            if (CollUtil.isEmpty(ruleList)) {
                log.error("[设备数据报警处理]设备信息无对应规则,无需处理");
                continue;
            }

            for (AlertRule alertRule : ruleList) {
                //第一层校验：判断生效时间
                String period = alertRule.getAlertEffectivePeriod();
                String[] split = period.split("~");
                LocalTime startTime = LocalTime.parse(split[0]);
                LocalTime endTime = LocalTime.parse(split[1]);
                LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();
                if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)) {
                    log.error("[定时报警规则校验]数据上报的时间不在规则校验内,无需处理");
                    continue;
                }

                //第二层校验：判断数据阈值
                int compare = NumberUtil.compare(Double.parseDouble(deviceData.getDataValue()), alertRule.getValue());
                String operator = alertRule.getOperator();
                if (operator.equals(">=") && compare >= 0 || operator.equals("<") && compare < 0) {

                    //第三层校验：查看沉默周期是否有值
                    String silentKey = CharSequenceUtil.format(CacheConstants.IOT_ALTER_SILENT, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                    String silentValue = redisTemplate.opsForValue().get(silentKey);
                    if (StringUtils.isNotBlank(silentValue)) {
                        log.error("[定时报警规则校验]沉默周期未结束,无需处理");
                        continue;
                    }
                    //第四层校验：查询已经报警的次数
                    String countKey = CharSequenceUtil.format(CacheConstants.IOT_ALTER_COUNT, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                    Long increment = redisTemplate.opsForValue().increment(countKey);
                    if (increment < alertRule.getDuration()) {
                        log.error("[定时报警规则校验]报警次数小于持续周期,无需处理");
                        continue;
                    }
                    redisTemplate.opsForValue().set(silentKey, "123", alertRule.getAlertSilentPeriod() * 60, TimeUnit.SECONDS);
                    redisTemplate.delete(countKey);

                    //判断是老人异常数据
                    List<Long> nursingIds = null;
                    if (alertRule.getAlertDataType() == 0) {
                        //判断是随身设备发出的异常数据
                        if (deviceData.getLocationType() == 0) {
                            nursingIds = deviceMapper.selectNursingIdsByMoveIotId(deviceData.getIotId());
                        } else {
                            //判断是固定设发出的异常数据
                            nursingIds = deviceMapper.selectNursingIdsByFitIotId(deviceData.getIotId());
                        }
                    } else {
                        //判断是设备异常数据
                        nursingIds = userMapper.selectUserIdByRoleName(roleRepair);
                    }
                    List<Long> userIds = userMapper.selectUserIdByRoleName(roleAdmin);
                    nursingIds.addAll(userIds);

                    //封装保存数据
                    AlertData alertData = new AlertData();
                    BeanUtils.copyProperties(deviceData, alertData);
                    alertData.setId(null);
                    alertData.setAlertRuleId(alertRule.getId());
                    String reasonTemplate = "功能{}{}{}，持续了{}个周期才报警，沉默周期：{}";
                    String alterReason = CharSequenceUtil.format(reasonTemplate, alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration(), alertRule.getAlertSilentPeriod());
                    alertData.setAlertReason(alterReason);
                    alertData.setType(alertRule.getAlertDataType());
                    alertData.setStatus(0);
                    List<AlertData> dataList = new ArrayList<>();
                    nursingIds.forEach(id -> {
                        AlertData data = new AlertData();
                        BeanUtils.copyProperties(alertData, data);
                        data.setUserId(id);
                        dataList.add(data);
                    });
                    boolean result = alertDataService.saveBatch(dataList);
                    if (!result) {
                        throw new BaseException("保存报警数据失败");
                    }
                    //通知消息给所有客户端用户（护理人员、维修工、超级管理员）进行提醒
                    AlertNotifyVo alertNotifyVo  = new AlertNotifyVo();
                    BeanUtils.copyProperties(deviceData, alertNotifyVo);
                    alertNotifyVo.setAlertDataType(alertRule.getAlertDataType());
                    alertNotifyVo.setIsAllConsumer(false);
                    alertNotifyVo.setFunctionName(alertRule.getFunctionName());
                    alertNotifyVo.setAlertDataType(alertNotifyVo.getAlertDataType());
                    alertNotifyVo.setNotifyType(1);
                    //发送完毕消息
                    nursingIds.forEach(x->{
                        webSocketServer.notifyMsgToClient(String.valueOf(x), JSONUtil.toJsonStr(alertNotifyVo));
                    });



                } else {
                    log.error("[定时报警规则校验]数据未达到规则阈值,无需处理");
                    continue;
                }

            }

        }

    }
}
